package spring.boot.btc.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import spring.boot.btc.model.Block;
import spring.boot.btc.model.Transaction;
import spring.boot.btc.model.TransactionInput;
import spring.boot.btc.model.TransactionOutput;
import spring.boot.btc.utils.BaseUtils;
import spring.boot.btc.utils.HashUtils;
import spring.boot.btc.utils.RsaUtils;

import java.util.List;

@Slf4j
@Service
public class BlockVerifyService {

    public boolean testBlock(Block block) {
        boolean res = true;
        StringBuilder sb = new StringBuilder();
        sb.append(block.getPreHash());
        sb.append(block.getMerkelHash());
        sb.append(block.getVersion());
        sb.append(block.getTimestamp());
        sb.append(block.getNonce());
        String testHash = HashUtils.sha256(sb.toString());
        if (!testHash.equals(block.getHash())) {
            res = false;
        }
        if (res && !block.getMerkelHash().equals(block.getTransaction().getHash())) {
            res = false;
        }
        if (res) {
            Transaction transaction = block.getTransaction();
            List<TransactionInput> inputList = transaction.getTransactionInputList();
            List<TransactionOutput> outputList = transaction.getTransactionOutputList();
            if (inputList.isEmpty() || outputList.isEmpty()) {
                res = false;
            }
            if (res) {
                StringBuilder builder = new StringBuilder();
                for (TransactionInput input : inputList) {
                    if (!testInput(input)) {
                        res = false;
                        break;
                    }
                    builder.append(input.getHash());
                }
                if (res) {
                    for (TransactionOutput output : outputList) {
                        if (!testOutput(output)) {
                            res = false;
                            break;
                        }
                        builder.append(output.getHash());
                    }
                    if (res) {
                        String txHash = HashUtils.sha256(builder.toString());
                        if (!txHash.equals(transaction.getHash())) {
                            res = false;
                        }
                    }
                }
            }
        }
        return res;
    }

    private boolean testInput(TransactionInput input) {
        String publicKey = input.getPublicKey();
        String address = input.getAddress();
        String sha256Key = HashUtils.sha256(publicKey);
        if (!address.equals(sha256Key)) {
            return false;
        }
        int type = input.getType();
        String sign = input.getSign();
        if (type == 0 && !"0".equals(sign)) {
            return false;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(publicKey);
        sb.append(address);
        sb.append(input.getMoney());
        sb.append(type);
        sb.append(input.getQuoteTransactionHash());
        sb.append(input.getQuoteTransactionOutputNum());
        byte[] data = sb.toString().getBytes();
        try {
            boolean testRes = RsaUtils.verifySignByPublicKey(data, BaseUtils.fromBase64ToByte(sign), BaseUtils.fromBase64ToByte(publicKey));
            if (!testRes) {
                return false;
            }
            StringBuilder builder = new StringBuilder();
            builder.append(publicKey);
            builder.append(address);
            builder.append(input.getMoney());
            builder.append(type);
            builder.append(sign);
            builder.append(input.getQuoteTransactionHash());
            builder.append(input.getQuoteTransactionOutputNum());
            String testInputHash = HashUtils.sha256(builder.toString());
            if (testInputHash.equals(input.getHash())) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }

    private boolean testOutput(TransactionOutput output) {
        String address = output.getAddress();
        String publicKey = output.getPublicKey();
        String sha256Key = HashUtils.sha256(publicKey);
        if (!sha256Key.equals(address)) {
            return false;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(publicKey);
        sb.append(address);
        sb.append(output.getMoney());
        String outputHash = HashUtils.sha256(sb.toString());
        if (!outputHash.equals(output.getHash())) {
            return false;
        }
        return true;
    }

}
